package org.openapi.service.impl;

import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.openapi.common.ApiException;
import org.openapi.common.ApiResult;
import org.openapi.consts.CacheKeys;
import org.openapi.consts.ExchangeMode;
import org.openapi.domain.ApiExchange;
import org.openapi.parser.DataParser;
import org.openapi.service.IDbService;
import org.openapi.service.IOpenExchangeService;
import org.openapi.utils.JsUtil;
import org.openapi.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.util.*;

@Service
@Slf4j
public class OpenExchangeServiceImpl implements IOpenExchangeService {
    @Autowired
    private IDbService dbService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 接收到读请求
     * @param exchange 请求接口对象
     * @param dataStr 请求内容
     * @return
     */
    @Override
    public ApiResult onRead(ApiExchange exchange, String dataStr){
        String sql = exchange.getRead();
        if(JsUtil.isFun(sql)){
            sql = JsUtil.eval(exchange.getRead(), dataStr);
        }
        List<Map<String,Object>> list = dbService.query(sql);
        return ApiResult.success(JsUtil.exec(exchange.getWrite(), list));
    }

    /**
     * 接收到写请求
     * @param exchange 请求接口对象
     * @param dataStr 请求内容
     * @return
     */
    @Override
    @Transactional
    public ApiResult onWrite(ApiExchange exchange, String dataStr){
        if(!JsUtil.isFun(exchange.getRead())){
            return ApiResult.error("接口有误");
        }

        int rows = 0;
        String retStr = JsUtil.eval(exchange.getRead(), dataStr);
        RelData data = DataParser.parseData(retStr);
        Map<String, Map> retMap = new HashMap<>();
        for(TableData table: data.getTables()){
            table.setIgnore(false);
            Map<String, Object> dataMap = table.getData();
            dbService.relData(retMap, dataMap);
            rows += dbService.insertData(table, true);
            retMap.put(table.getTable(), dataMap);
        }
        return ApiResult.success(JsUtil.exec(exchange.getWrite(), retMap));
    }

    /**
     * 主动执行一次
     * @param exchange 请求对象
     * @return
     */
    @Override
    public ApiResult run(ApiExchange exchange) {
        Object data = redisTemplate.opsForHash().get(CacheKeys.CACHE_API_DATA, exchange.getId().toString());
        Map apiCache = data == null ? new HashMap() : (Map) data;
        ApiResult ret = null;
        if(ExchangeMode.ACTIVE_READ.equalsIgnoreCase(exchange.getMode())) {
            ret = runRead(exchange, apiCache);
        }else if(ExchangeMode.ACTIVE_WRITE.equalsIgnoreCase(exchange.getMode())){
            ret = runWrite(exchange, apiCache);
        }else{
            throw new ApiException("不支持的接口操作");
        }

        if(ret.getCode() == 0){
            redisTemplate.opsForHash().put(CacheKeys.CACHE_API_DATA, exchange.getId().toString(), apiCache);
        }
        return ret;
    }

    /**
     * 主动从对方读数据
     * @param exchange
     * @return
     */
    private ApiResult runRead(ApiExchange exchange, Map apiCache){
        //生成请求参数
        String retStr = JsUtil.exec(exchange.getRead(), apiCache);
        JSONObject params = JSONObject.parseObject(retStr);
        HttpHeaders headers = new HttpHeaders();
        if(params.containsKey("headers")){
            for(String hk:params.getJSONObject("headers").keySet()) {
                headers.add(hk, params.getJSONObject("headers").getString(hk));
            }
        }
        String method = params.getString("method").toUpperCase();
        String uri = params.getString("uri");
        ResponseEntity<String> res;
        //发起请求
        if(method.equalsIgnoreCase("POST") && params.containsKey("data")){
            HttpEntity<JSONObject> entity = new HttpEntity<JSONObject>(params.getJSONObject("data"), headers);
            res = restTemplate.exchange(uri, HttpMethod.resolve(method), entity, String.class);
        }else{
            HttpEntity<HttpHeaders> entity = new HttpEntity<HttpHeaders>(headers);
            res = restTemplate.exchange(uri, HttpMethod.resolve(method), entity, String.class);
        }
        if(res.getStatusCodeValue() != 200){
            log.error("请求失败{}-{}", uri, res.getStatusCodeValue());
            return ApiResult.error(res.getStatusCodeValue(), res.getStatusCode().getReasonPhrase());
        }else{
            log.info("请求成功，响应 {}", res.getBody());
        }
        //解析响应数据
        retStr = JsUtil.exec(exchange.getWrite(), res.getBody());
        params = JSONObject.parseObject(retStr);
        reCache(apiCache, params);
        //本地写入
        RelData data = DataParser.parseData(params.toJSONString());
        Map<String, Map> retMap = new HashMap<>();
        for(TableData table: data.getTables()){
            table.setIgnore(false);
            Map<String, Object> dataMap = table.getData();
            dbService.relData(retMap, dataMap);
            dbService.insertData(table, true);
            retMap.put(table.getTable(), dataMap);
        }
        return ApiResult.success();
    }

    /**
     * 主动向对方写数据
     * @param exchange
     * @return
     */
    private ApiResult runWrite(ApiExchange exchange, Map apiCache){
        //生成查询语句
        String retStr = JsUtil.exec(exchange.getRead(), apiCache);
        List<Map<String,Object>> list = dbService.query(retStr);

        //生成请求数据
        Map<String,Object> ps = new HashMap<>();
        ps.put("cache", apiCache);
        ps.put("list", list);
        retStr = JsUtil.exec(exchange.getWrite(), ps);
        JSONObject retData = JSONObject.parseObject(retStr);
        reCache(apiCache, retData);

        JSONObject params = JSONObject.parseObject(retStr);
        HttpHeaders headers = new HttpHeaders();
        if(params.containsKey("headers")){
            for(String hk:params.getJSONObject("headers").keySet()) {
                headers.add(hk, params.getJSONObject("headers").getString(hk));
            }
        }
        String method = params.getString("method").toUpperCase();
        String uri = params.getString("uri");
        ResponseEntity<String> res;
        //发起请求
        if(method.equalsIgnoreCase("POST") && params.containsKey("body")){
            HttpEntity<JSONObject> entity = new HttpEntity<JSONObject>(params.getJSONObject("body"), headers);
            res = restTemplate.exchange(uri, HttpMethod.resolve(method), entity, String.class);
        }else{
            HttpEntity<HttpHeaders> entity = new HttpEntity<HttpHeaders>(headers);
            res = restTemplate.exchange(uri, HttpMethod.resolve(method), entity, String.class);
        }

        if(res.getStatusCodeValue() != 200){
            log.error("请求失败{}-{}", uri, res.getStatusCodeValue());
            return ApiResult.error(res.getStatusCodeValue(), res.getStatusCode().getReasonPhrase());
        }else{
            log.info("请求成功，响应 {}", res.getBody());
        }
        return ApiResult.success();
    }

    private void reCache(Map apiCache, JSONObject newCache){
        if(!newCache.containsKey("@cache")){
            return;
        }
        for(String k:newCache.getJSONObject("@cache").keySet()){
            Object v = newCache.getJSONObject("@cache").get(k);
            if(v != null && v.toString().equalsIgnoreCase("_remove")){
                apiCache.remove(k);
            }else{
                apiCache.put(k, v);
            }
        }
        newCache.remove("@cache");
    }
}
